#include "daq.h"
#include "comPort.h"
#include <stdio.h>
#include <vector>
#include "../global.h"

using namespace std;
using namespace DAQ_namespace;

// Global Variables
uint8_t tempBuffer[65535];

/*
#define UP_TO_DATE              0x00
#define MSG_NOT_FOUND           0x80
#define INPUT_MSG_FOUND         0x01
#define OTHER_MSG_FOUND         0x02
*/

//grabs lastest data from the buffer
READ_ENUM comPortGetLast()
{
    uint8_t packet[32];//allocate more than enough data
    uint8_t packetLength;
    uint16_t i;
    READ_ENUM addInfo = MSG_NOT_FOUND; //this is used to determine if collateral msgs were read

    //grab the latest and greates to figure out the size of unread data.
    bufRx.receiveFunc();

    if(bufRx.bytesInBuffer > (packetSizeLookup(inputSuperPacket_mh)*2-1))//threshold of just under 2 input packets
    {

        //config vTail pointer
        if(bufRx.head  - (packetSizeLookup(inputSuperPacket_mh)*2-1) > (uint8_t *)bufRx.bufferBegin )
        {
            //if the head pointer is far enough ahead of the buffer beginnning address
            //we simply go back some distance from the head to place the vTail
            bufRx.vTail = bufRx.head - (packetSizeLookup(inputSuperPacket_mh)*2-1);
        }
        else
        {
            //if the buffer head is too close to the buffer beginning address we have to
            //place the vTail pointer in reference to the buffer end address too

            uint8_t offset;

            offset = (uint8_t) (bufRx.head - (uint8_t *)bufRx.bufferBegin);
            bufRx.vTail = (uint8_t *)bufRx.bufferEnd - ( (packetSizeLookup(inputSuperPacket_mh)*2-1) - offset);
        }

        //read last packet

        //find the packet header
startRead:
        do
        {
            packet[0] = bufRx.vBufferRead();
            if(bufRx.vTail == bufRx.head)
            {
                return addInfo; //input message not found
            }
        }while(packet[0] != packetHeader);

        //read the msg header
        packet[1] = bufRx.bufferRead();
        if(bufRx.vTail == bufRx.head)
        {
            return addInfo; //input message not found
        }

        //determine if the message header is valid
        if((packetLength = packetSizeLookup(packet[1])));
        else goto startRead;//revert to the beginning of the read process until the latest data is expended

        for(i=2;i<packetLength;i++)
        {
            //populate the packet with data
            packet[i] = bufRx.vBufferRead();

            if(bufRx.vTail == bufRx.head)
            {
                return addInfo; //input message not found
            }
        }

        if (crcCheck( (uint8_t *)packet,packetLength))
        {

            //process data
            processData((uint8_t *)&packet);

            if(packet[1] == inputSuperPacket_mh)
            {
                bufRx.flush();
                if (addInfo == OTHER_MSG_FOUND)
                {
                    return INPUT_AND_OTHER_MSG_FOUND;
                }
                return INPUT_MSG_FOUND;
            }
            else
            {
                addInfo = OTHER_MSG_FOUND;  //msg found but not an input msg
                goto startRead;             //read the rest of the data to see if an input packet still exists
            }
        }


    }
    else
    {
        //data is up to date
        return UP_TO_DATE;
    }



}

READ_ENUM comPortGet()
{
    uint8_t packet[32];//allocate more than enough data
    uint8_t i;
    uint8_t packetLength;

    do
    {
        //if there is no data wait until there is
        while(bufRx.bytesInBuffer == 0)
        {
            bufRx.receiveFunc();
        }
        //read data until a packet header is reached
        packet[0] = bufRx.bufferRead();

    }while(packet[0] != packetHeader);

    //if there is no data wait until there is
    while(bufRx.bytesInBuffer == 0)
    {
        bufRx.receiveFunc();
    }

    //read in the packet "message" header
    packet[1] = bufRx.bufferRead();

    //determine if the message header is valid
    if((packetLength = packetSizeLookup(packet[1])));
    else return MSG_NOT_FOUND;

    //i is equal to 2 initially since the packet header and the msg header
    //are used respectively to signify the existance of the message,
    //and to specify the number of bytes are contained in the message;

    for(i=2;i<packetLength;i++)
    {
        //if there is no data wait until there is
        while(bufRx.bytesInBuffer == 0)
        {
            bufRx.receiveFunc();
        }

        //read in the number of bytes per the message header
        packet[i] = bufRx.bufferRead();
    }

    //check to see if the checksum is valid
    if (crcCheck( (uint8_t *)packet,packetLength))
    {
        //process data
        processData((uint8_t *)&packet);
        if(packet[1] == inputSuperPacket_mh) return INPUT_MSG_FOUND;
        else                                 return OTHER_MSG_FOUND;
    }

    //checksum was invalid if here
    return MSG_NOT_FOUND;
}
/* ---<>-----------------------------------------------------------------------------------
		comPortInit
-------------------------------------------------------------------------------------------- */
int  comPortInit(DWORD baud, uint16_t rxTime, uint16_t txTime)
{
    DWORD devIndex = 0; // first device
    char buffer[64]; // more than enough room!
	int error;

    error = FTError( FT_ListDevices((PVOID)devIndex,buffer,FT_LIST_BY_INDEX|FT_OPEN_BY_SERIAL_NUMBER) ); if (error) {return error;}
    error = FTError( FT_OpenEx(buffer,FT_OPEN_BY_SERIAL_NUMBER,&handle) ); if (error) {return error;}

    printf("Device %s opened! \n",buffer);
	printf("FT_Handle %d \n",handle);
	
    error = FTError( FT_ResetDevice(handle) ); if (error) {return error;}
    error = FTError( FT_SetBaudRate(handle, baud) ); if (error) {return error;}
    error = FTError( FT_SetDataCharacteristics(handle, FT_BITS_8, FT_STOP_BITS_1,FT_PARITY_NONE) ); if (error) {return error;}
    error = FTError( FT_SetFlowControl(handle, FT_FLOW_NONE, FT_FLOW_XON_XOFF, FT_FLOW_XON_XOFF) ); if (error) {return error;}

    error = FTError( FT_SetUSBParameters(handle, 64, 0) ); if (error) {return error;}

//  This function is not supported by many FTDI devices.  It's best not to depend on it
  error = FTError( FT_SetLatencyTimer(handle, 0) ); if (error) {return error;}

    error = FTError( FT_Purge(handle, FT_PURGE_RX | FT_PURGE_TX) ); if (error) {return error;}
    error = FTError( FT_SetDtr(handle) ); if (error) {return error;}
//    error = FTError( FT_SetRts(handle) ); if (error) {return error;}

    //added functionality to change the timeout.
    //a handful of mS seems to be preferable (3-5)
    error = FTError( FT_SetTimeouts(handle, rxTime, txTime) ); if (error) {return error;}
    return 0;
}

/* ---<>-----------------------------------------------------------------------------------
		comPorPut()
-------------------------------------------------------------------------------------------- */
uint16_t comPortPut(uint8_t *packet, uint8_t length)
{
    DWORD bytesSent;

    FTError(FT_Write(handle,packet,length,&bytesSent));
    return bytesSent;
}

/* ---<>-----------------------------------------------------------------------------------
		comPortClose()
-------------------------------------------------------------------------------------------- */
void comPortClose()
{
    FT_Close(handle);
}

/* ---<>-----------------------------------------------------------------------------------
		FTError()
-------------------------------------------------------------------------------------------- */
int FTError(FT_STATUS status)
{
    if (status != FT_OK)
    {
        printf("FTDI Error: ");

        switch (status)
        {
        case 1:
            printf("Invalid handle! \n");
            break;
        case 2:
            printf("Device not found! \n");
            break;
        case 3:
            printf("Device not opened! \n");
            break;
        default:
            printf("Unknown error! \n");
        }
       return status;
    }
	return 0;
}

/* ---<>-----------------------------------------------------------------------------------
		processData() 
-------------------------------------------------------------------------------------------- */
void processData(uint8_t *packet)
{

    switch(packet[1])
    {

    //determine is the receive packet is an ack
    case (ack_mh):

        //create a convenient and readable way of working with the data packet
        xtrg_t *packetPtr;
        //map the ack message pointer to the packet passed to the function
        packetPtr = (xtrg_t *)&packet[0];
        switch(packetPtr->ackedHeader)
        {
            case(adcInit_mh):

            DAQ.analogInConfig(packetPtr->status);

            break;

            case(dacEnable_mh):

            DAQ.analogOutConfig(packetPtr->status);

            break;

            case(digitalInEnable_mh):

            DAQ.digitalInConfig(packetPtr->status);

            break;

            case(digitalOutEnable_mh):

            DAQ.digitalOutConfig(packetPtr->status);

            break;

            case(encInit_mh):

            DAQ.encoderConfig(packetPtr->status);

            break;

            case(tmrSetFreq_mh):

            DAQ.tmrConfig(packetPtr->status);

            break;

            case(startProgram_mh):

            DAQ.startConfig(packetPtr->status);

            break;

            case(stopProgram_mh):

            DAQ.stopConfig(packetPtr->status);

            break;

            case(resetDaq_mh):
            asm("nop");
            break;

            case(motorEnable_mh):

            DAQ.motorConfig(packetPtr->status);

            break;

            case(servoEnable_mh):

            DAQ.servoConfig(packetPtr->status);

            break;

            case(outputSuperPacket_mh):

            break;

            case(actuatorSuperPacket_mh):

            break;
        }
    break;

    case(daqReady_mh):

        DAQ.readyConfig();

    break;

    case (inputSuperPacket_mh):
		DAQ.inputSuperPacketHandler(packet);
		break;
    }
}
